package com.gitee.wsl.data.pool

import com.gitee.wsl.ext.K
import com.gitee.wsl.ext.runBlocking
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.withContext
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract

interface AsyncPool<T>:AutoCloseable {

    val capacity: Int

    /**
     * @return An instance from the pool if such, null otherwise.
     */
    suspend fun acquire(): T?

    /**
     * Release an instance to the pool.
     *
     * @param instance The instance to release.
     * @return Whether the instance was put in the pool.
     * @throws IllegalStateException If the instance is already in the pool.
     */
    suspend fun release(instance: T): Boolean

    /**
     * Try releasing all instances at the same time
     *
     * @param variables the variables to release
     * @param count     the number of variables to release
     */
    suspend fun releaseAll(variables: List<T>)

    suspend fun dispose()

    /**
     * Does pool dispose
     */
    override fun close() {
        K.runBlocking {
            dispose()
        }
    }
}

/**
 * Borrows and instance of [T] from the pool, invokes [block] with it and finally recycles it
 */
@OptIn(ExperimentalContracts::class)
suspend inline fun <T, R> AsyncPool<T>.useInstance(block:  (T?) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }

    val item = acquire()

    try {
        return block(item)
    } finally {
        if (item != null) {
            withContext(NonCancellable) {
                release(item)
            }
        }
    }
}